package com.thunder.reader.provider;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import com.thunder.reader.util.Utility;

/**
 * Using HandlerThread and Thread implements a service like server thread is convenient to use though,
 * it is very hard to control its life-circle. When to stop itself, when to restart it.
 * Currently, it is only visible to Provider;
 * @author AM
 */
class ImageDownloadServer extends HandlerThread {
    private Context mContext;
    private Handler mHandler;
    private ExecutorService mExecutor;

    public ImageDownloadServer(final Context context) {
        super("ImageDownloadServer");
        mContext = context;
    }

    @Override
    public void start() {
        mExecutor = Executors.newCachedThreadPool();
        super.start();
    }
    @Override
    protected void onLooperPrepared() {
        super.onLooperPrepared();
        mHandler = new Handler(getLooper(), new Handler.Callback() {
            public boolean handleMessage(Message msg) {
                // TODO: use ThreadPool is a better idea
                mExecutor.execute(new ImageDownloader(mContext, (Uri) msg.obj));
                return false;
            }
        });
    }
    
    public void exit() {
        getLooper().quit();
        mExecutor.shutdown();
    }

    public void execute(Uri uri) {
        if (mHandler != null) {
            Message msg = Message.obtain();
            msg.obj = uri;
            mHandler.sendMessage(msg);
        }
    }
}

class ImageDownloader implements Runnable {
    private Context mContext;
    private Uri mUri;
    private String TAG = "ImageDownloader";

    public ImageDownloader(Context context, Uri uri) {
        mContext = context;
        mUri = uri;
    }

    public void run() {
        if (!Utility.sdcardWritable()) {
            return;
        }
        final String url = getImageUrl();
        if (TextUtils.isEmpty(url)) {
            return;
        }
        try {
            InputStream image = fetchImageContent(url);
            if (image == null || !Utility.sdcardWritable()) {
                return;
            }
            final File file = getOutputFile(url);
            writeImageToStorage(image, file);
            image.close();
            addIamgeToDatabase(file);
        } catch (MalformedURLException e) {
            Log.e(TAG, "exception caught ", e);
        } catch (ClientProtocolException e) {
            Log.e(TAG, "exception caught ", e);
        } catch (IOException e) {
            Log.e(TAG, "exception caught, ", e);
        }
    }

    private InputStream fetchImageContent(final String url)
            throws MalformedURLException, IOException, ClientProtocolException {
        Log.e(TAG , "fetching " + url);
        Log.e(TAG, "fecthing for " + mUri);
        HttpRequest req = new HttpGet(url);
        HttpClient client = new DefaultHttpClient();
        URL u = new URL(url);
        HttpHost host = new HttpHost(u.getHost());
        HttpResponse response = client.execute(host, req);
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            Log.e(TAG, "fetchImageContent, res code " + response.getStatusLine().getStatusCode() + ", res msg " + 
                    response.getStatusLine().getReasonPhrase());
            return null;
        }
        HttpEntity entity = response.getEntity();
//        Log.e(TAG, "entity content length: " + entity.getContentLength());
//        Log.e(TAG, "entity content type : " + entity.getContentType());
        return entity.getContent();
    }

    private void addIamgeToDatabase(final File file) {
        if (!file.exists() || file.length() <= 0) {
            return;
        }
        ContentValues values = new ContentValues(1);
        values.put(Feed.Image._DATA, file.getAbsolutePath());
        final Uri imageUri = mContext.getContentResolver().insert(Feed.Image.CONTENT_URI, values);
        values = new ContentValues(1);
        values.put(Feed.Article.IMAGE_ID, imageUri.getPathSegments().get(1));
        mContext.getContentResolver().update(mUri, values, null, null);
        Log.e(TAG, "There is a new image for " + mUri + " image id " + imageUri + ", path " + file.getAbsolutePath());
    }

    private void writeImageToStorage(InputStream content, final File file)
            throws FileNotFoundException, IOException {
        OutputStream out = new FileOutputStream(file);
        byte[] buffer = new byte[8912];
        int seg = 0;
        while ((seg = content.read(buffer)) != -1) {
            out.write(buffer, 0, seg);
        }
        out.close();
    }

    private File getOutputFile(final String url) {
        final File dir = mContext.getExternalFilesDir(null);
        final String filename = url.substring(url.lastIndexOf('/') + 1);
        final File file = ensureFile(dir, filename);
        return file;
    }

    // TODO: need to devise better idea to avoid overwrite existing files.
    private File ensureFile(File dir, String filename) {
        String[] files = dir.list();
        for (String f : files) {
            if (f.equals(filename)) {
                filename = "1_" + filename;
                break;
            }
        }
        return new File(dir, filename);
    }

    private String getImageUrl() {
        final Cursor c = mContext.getContentResolver().query(mUri, new String[]{Feed.Article.IMAGE_URL}, null, null, null);
        c.moveToFirst();
        final String url = c.getString(0);
        c.close();
        return url;
    }
}